package org.opu.gui.docviewer.table;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;

/**
 *  
 * @author yura
 */
public class FreeTableDocument extends TableDocument {

    /**
     *  Pass at end of the document where the table will be inserted,
     * row count and width of the table’s columns. Size of the array
     * defines column count in the table.
     *
     * @param rowCount number of row in table
     * @param colWidths arrays of cells width
     * @return table map
     */
    public TableMap insertTable(int[][] colWidths){
        try {
            return this.insertTable(this.lastPos(), colWidths);
        } catch (BadLocationException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     *  Pass offset in the document where the table will be inserted,
     * row count and width of the table’s columns. Size of the array
     * defines column count in the table.
     *
     * @param offset the starting offset >= 0
     * @param rowCount number of row in table
     * @param colWidths arrays of cells width
     * @return table map
     * @throws BadLocationException for an invalid starting offset
     */
    public TableMap insertTable(int offset, int[][] colWidths)
            throws BadLocationException {
        TableMap map = new TableMap();
        SimpleAttributeSet attrs = new SimpleAttributeSet();

        ArrayList tableSpecs = new ArrayList();
        tableSpecs.add(new ElementSpec(attrs, ElementSpec.EndTagType)); //close paragraph tag

        SimpleAttributeSet tableAttrs = new SimpleAttributeSet();
        tableAttrs.addAttribute(ElementNameAttribute, ELEMENT_NAME_TABLE);
        ElementSpec tableStart = new ElementSpec(tableAttrs, ElementSpec.StartTagType);
        tableSpecs.add(tableStart); //start table tag

        fillRowSpecs(map, tableSpecs, offset, colWidths);

        ElementSpec tableEnd = new ElementSpec(tableAttrs, ElementSpec.EndTagType);
        tableSpecs.add(tableEnd); //end table tag

        tableSpecs.add(new ElementSpec(attrs, ElementSpec.StartTagType)); //open new paragraph after table

        ElementSpec[] spec = new ElementSpec[tableSpecs.size()];
        tableSpecs.toArray(spec);

        this.insert(offset, spec);
        return map;
    }

    /**
     *  Appending rows to tableSpecs list
     *
     * @param map table map
     * @param tableSpecs list where rows will be added
     * @param offset the starting offset
     * @param colWidths  array of cells width
     */
    protected void fillRowSpecs(TableMap map, ArrayList tableSpecs,
            int offset, int[][] colWidths) {
        SimpleAttributeSet rowAttrs = new SimpleAttributeSet();
        rowAttrs.addAttribute(ElementNameAttribute, ELEMENT_NAME_ROW);

        int kol = 0;
        for (int i = 0; i < colWidths.length; i++) {
            ElementSpec rowStart = new ElementSpec(rowAttrs, ElementSpec.StartTagType);
            tableSpecs.add(rowStart);

            fillCellSpecs(tableSpecs, colWidths[i]);

            ElementSpec rowEnd = new ElementSpec(rowAttrs, ElementSpec.EndTagType);
            tableSpecs.add(rowEnd);

            map.addRow();
            for (int j = 0; j < colWidths[i].length; j++) {
                map.addCell(offset + kol++);
            }
        }

    }

    /**
     * Returns the plase where new data can be appended
     *
     * @return the plase where new data can be appended
     */
    public int lastPos() {
        return getLength();
    }

    /**
     * Inserts new elements in bulk.
     *
     * @param offset the starting offset >= 0
     * @param element the element data
     * @throws BadLocationException for an invalid starting offset
     */
    public void insertElement(int offset, ElementSpec element)
            throws BadLocationException {
        this.insert(offset, new ElementSpec[]{element});
    }

    /**
     *  Inserts some content into the end of the document.
     *
     * @param str the string to insert; does nothing with null/empty strings
     * @param a the attributes for the inserted content
     */
    public void insertString(String str, AttributeSet a) {
        try {
            super.insertString(lastPos(), str, a);
        } catch (BadLocationException ex) {
            throw new RuntimeException(ex);
        }
    }

// <editor-fold defaultstate="collapsed" desc="TableMap class">

    /**
     *  Map with identivicators of numbers of cells in created table
     */
    public static class TableMap {

        private ArrayList<ArrayList<Integer>> map = new ArrayList<ArrayList<Integer>>();

        // <editor-fold defaultstate="collapsed" desc="private methods for control map">
        /**
         *  Add new row at the end of the map
         * 
         * @return true if row was added 
         */
        private boolean addRow() {
            return map.add(new ArrayList<Integer>());
        }

        /**
         *  Add new cell at the end of the last row at the map
         *
         * @return true if cell was added
         */
        private boolean addCell(int value) {
            return map.get(map.size() - 1).add(value);
        }

        /**
         *  Shift at the len position from the pos position to end
         *
         * @param pos where from started to shifting
         * @param len number of shifting
         */
        private void shift(int pos, int len) {
            for (int i = pos; i < map.size(); i++) {
                ArrayList<Integer> get = map.get(i);
                for (int j = pos; j < get.size(); j++) {
                    int val = get.get(j) + len;
                    get.set(j, val);
                }
            }
        }// </editor-fold>

        /**
         * Return number of rows at map
         *
         * @return number of rows at map
         */
        public int row() {
            return map.size();
        }

        /**
         *  Return number of cells at row "row"
         * 
         * @param row number of row where be calculated number of cells
         * @return number of cells at row "row"
         */
        public int cell(int row) {
            return map.get(row).size();
        }

        /**
         *  Return value of row "row" and cell "cell"
         * 
         * @param row row where value defined
         * @param cell cell where value defined
         * @return value of row "row" and cell "cell"
         */
        public int value(int row, int cell) {
            return map.get(row).get(cell);
        }

        @Override
        public String toString() {
            return map.toString();
        }

    }// </editor-fold>

}
